﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Reflection;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace MCYFlashApp
{
    public class ImageProperty
    {
        private Type _imageType = null;

        private ImageBox _imageBox;

        /// <summary>
        /// The parent imagebox for this property panel
        /// </summary>
        public ImageBox ImageBox
        {
            get
            {
                return _imageBox;
            }
            set
            {
                _imageBox = value;
            }
        }

        private int _width = 0;
        /// <summary>
        /// 图像宽
        /// </summary>
        public int Width
        {
            get
            {
                return _width;
            }
        }

        private int _height = 0;
        /// <summary>
        /// 图像高
        /// </summary>
        public int Height
        {
            get
            {
                return _height;
            }
        }

        private string _typeOfColor = "";
        /// <summary>
        /// 图像颜色
        /// </summary>
        public string TypeOfColor
        {
            get
            {
                return _typeOfColor;
            }
        }

        private string _typeOfDepth = "";
        public string TypeOfDepth
        {
            get
            {
                return _typeOfDepth;
            }
        }

        private object _colorIntensity = null;


        public Point CurrentMouse
        {
            get;
            set;
        }

        private bool IsSubTypeOf(Type objType, Type typeToCheck)
        {
            while (objType != typeToCheck)
            {
                if (objType.IsGenericType && objType.GetGenericTypeDefinition() == typeToCheck)
                {
                    return true;
                }
                objType = objType.BaseType;
                if (objType == null)
                {
                    return false;
                }
            }
            return true;
        }


        public void SetImage(IImage image)
        {

            #region display the size of the image
            if (image != null)
            {
                Size size = image.Size;
                _width = size.Width;
                _height = size.Height;
            }
            else
            {
                _width = 0;
                _height = 0;
            }
            #endregion

            #region display the color type of the image
            if (image != null)
            {
                Type colorType = ReflectIImage.GetTypeOfColor(image);
                Object[] colorAttributes = colorType.GetCustomAttributes(typeof(ColorInfoAttribute), true);
                if (colorAttributes.Length > 0)
                {
                    ColorInfoAttribute info = (ColorInfoAttribute)colorAttributes[0];
                    _typeOfColor = info.ConversionCodename;
                }
                else
                {
                    _typeOfColor = "";
                }

                Type colorDepth = ReflectIImage.GetTypeOfDepth(image);
                _typeOfDepth = colorDepth.Name;
            }
            else
            {
                _typeOfColor = string.Empty;
                _typeOfDepth = string.Empty;
            }
            #endregion

            #region check if image is a subclass of CvArr type
            if (image != null)
            {
                Type imgType = image.GetType();
                if (IsSubTypeOf(imgType, typeof(CvArray<>)))
                    _imageType = typeof(CvArray<>);
                else if (IsSubTypeOf(imgType, typeof(Mat)))
                    _imageType = typeof(Mat);
                else if (IsSubTypeOf(imgType, typeof(UMat)))
                    _imageType = typeof(UMat);
                else
                {
                    _imageType = null;
                }
            }
            else
            {
                _imageType = null;

            }
            #endregion
        }

        /// <summary>
        /// Set the mouse position over the image. 
        /// It also set the color intensity of the pixel on the image where is mouse is at
        /// </summary>
        /// <param name="location">The location of the mouse on the image</param>
        public void SetMousePositionOnImage(Point location)
        {
            IImage img = _imageBox.DisplayedImage;
            Size size = img.Size;
            location.X = Math.Max(Math.Min(location.X, size.Width - 1), 0);
            location.Y = Math.Max(Math.Min(location.Y, size.Height - 1), 0);
            CurrentMouse = location;


            if (_imageType == typeof(CvArray<>))
            {
                MCvScalar scalar = CvInvoke.cvGet2D(img.Ptr, location.Y, location.X);

                _colorIntensity = BufferToString(scalar.ToArray(), img.NumberOfChannels);
            }
            else if (_imageType == typeof(Mat))
            {
                Mat mat = img as Mat;
                RenderIntensityForMat(mat, location);
            }
            else if (_imageType == typeof(UMat))
            {
                UMat umat = img as UMat;
                using (Mat mat = umat.GetMat(AccessType.Read))
                {
                    RenderIntensityForMat(mat, location);
                }
            }
            else
            {
                _colorIntensity = String.Empty;
            }
        }
        private void RenderIntensityForMat(Mat mat, Point location)
        {
            byte[] raw = mat.GetData(location.Y, location.X);

            if (mat.Depth == DepthType.Cv8U)
            {
                _colorIntensity = BufferToString(raw, mat.NumberOfChannels);
            }
            else if (mat.Depth == DepthType.Cv8S)
            {
                sbyte[] data = new sbyte[mat.NumberOfChannels];
                GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
                Marshal.Copy(raw, 0, handle.AddrOfPinnedObject(), mat.ElementSize);
                handle.Free();
                _colorIntensity = BufferToString(data, data.Length);
            }
            else if (mat.Depth == DepthType.Cv16S)
            {
                GCHandle handle = GCHandle.Alloc(raw, GCHandleType.Pinned);
                short[] data = new short[mat.NumberOfChannels];
                Marshal.Copy(handle.AddrOfPinnedObject(), data, 0, data.Length);
                handle.Free();
                _colorIntensity = BufferToString(data, data.Length);
            }
            else if (mat.Depth == DepthType.Cv16U)
            {
                UInt16[] data = new UInt16[mat.NumberOfChannels];
                GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
                Marshal.Copy(raw, 0, handle.AddrOfPinnedObject(), mat.ElementSize);
                handle.Free();
                _colorIntensity = BufferToString(data, data.Length);
            }
            else if (mat.Depth == DepthType.Cv32F)
            {
                GCHandle handle = GCHandle.Alloc(raw, GCHandleType.Pinned);
                float[] floatData = new float[mat.NumberOfChannels];
                Marshal.Copy(handle.AddrOfPinnedObject(), floatData, 0, floatData.Length);
                handle.Free();
                _colorIntensity = BufferToString(floatData, floatData.Length);
            }
            else if (mat.Depth == DepthType.Cv32S)
            {
                int[] data = new int[mat.NumberOfChannels];
                GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
                Marshal.Copy(raw, 0, handle.AddrOfPinnedObject(), mat.ElementSize);
                handle.Free();
                _colorIntensity = BufferToString(data, data.Length);
            }
            else if (mat.Depth == DepthType.Cv64F)
            {
                GCHandle handle = GCHandle.Alloc(raw, GCHandleType.Pinned);
                double[] doubleData = new double[mat.NumberOfChannels];
                Marshal.Copy(handle.AddrOfPinnedObject(), doubleData, 0, doubleData.Length);
                handle.Free();
                _colorIntensity = BufferToString(doubleData, doubleData.Length);
            }
            else
            {
                _colorIntensity = String.Empty;
            }
        }

        private object[] BufferToString<T>(T[] data, int numberOfChannels)
        {
            List<object> lst = new List<object>();            
            for (int i = 0; i < numberOfChannels; i++)
                lst.Add(data[i]);

            return lst.ToArray();
        }
    }
}
